package com.yisu.expressway.network;

import android.content.Context;
import android.graphics.Bitmap;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Build;
import android.telephony.TelephonyManager;

import com.android.volley.Request;
import com.android.volley.Response;
import com.google.gson.reflect.TypeToken;
import com.will.network.RequestManager;
import com.will.network.images.ImageCacheManager;
import com.will.network.images.ImageLruMemoryCache;
import com.yisu.expressway.application.ExApplication;
import com.yisu.expressway.login.LoginManager;
import com.yisu.expressway.utils.HBLog;
import com.yisu.expressway.utils.MD5Util;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;


/**
 * Created by theodore on 16-9-13.
 */
public class ApiClientHelper {


    private static final int DISK_IMAGECACHE_SIZE = 1024*1024*128;//64M
    private static final int MEMORY_IMAGECACHE_SIZE = ImageLruMemoryCache.getDefaultLruCacheSize() * 3;//1024*1024*48;//48M
    private static Bitmap.CompressFormat DISK_IMAGECACHE_COMPRESS_FORMAT = Bitmap.CompressFormat.PNG;
    private static final int DISK_IMAGECACHE_QUALITY = 100;  //PNG is lossless so quality is ignored but must be provided
    private static String deviceUniqueId;

    public static void init(Context context){
        // volley init
        RequestManager.getInstance().init(context);

        // image cache init
        ImageCacheManager.getInstance().init(context
                , context.getPackageCodePath()
                , MEMORY_IMAGECACHE_SIZE
                , DISK_IMAGECACHE_SIZE
                , DISK_IMAGECACHE_COMPRESS_FORMAT
                , DISK_IMAGECACHE_QUALITY);
    }

    public static Map<String, String> getGeneralHeader(){
        HashMap<String, String> generalHeaders = new HashMap<>();
        generalHeaders.put(HttpHeaderConstants.HEADER_AGENT, NetworkConfig.getWebUserAgent());
        generalHeaders.put(HttpHeaderConstants.HEADER_API_VERSION, HttpHeaderConstants.HTTP_API_VERSION);
        if (getUniUserId(ExApplication.getInstance()) != null)//(PhoneState.Instance().getIMSI() != null)
            generalHeaders.put(HttpHeaderConstants.HEADER_CLIENT_ID, getUniUserId(ExApplication.getInstance()));
        if (LoginManager.getUserId() != null) {
            generalHeaders.put(HttpHeaderConstants.HEADER_USER_ID, LoginManager.getUserId());
        }
        if (LoginManager.getLongToken() != null) {
            generalHeaders.put(HttpHeaderConstants.HEADER_LONG_TOKEN, LoginManager.getLongToken());
        }

        if (mListener != null){
            mListener.onHeadGenerate(generalHeaders);
        }
        return generalHeaders;
    }


    public static final String TAG = "ApiClientHelper";


    private static RequestManager mReqManager = RequestManager.getInstance();
    public static void dequeue(Object tag) {
        mReqManager.cancelPendingRequests(tag);
    }

    /**
     * Response is ApiResult<T>, T is result model.
     * @param url
     * @param listener
     * @param errorListener
     * @param dealListener
     * @param tag
     * @param retries
     * @param timeout
     * @param <T>
     */
    public static<T> ApiResultRequest getApi(final String url, TypeToken<T> resultType, ApiModelRequest.ResponseDealListener<ApiResult<T>> dealListener, Response.Listener<ApiResult<T>> listener, Response.ErrorListener errorListener, Object tag, int retries, int timeout){
        HBLog.w(TAG, "getUrl=========>" + url);
        ApiResultRequest<T> request = new ApiResultRequest<>(url, resultType, dealListener, listener, errorListener);
        request.setCustomHeaders(getGeneralHeader());
        mReqManager.addToRequestQueue(request, tag, retries, timeout);

        return request;
    }
    public static<T> ApiResultRequest getApi(final String url, TypeToken<T> resultType, ApiModelRequest.ResponseDealListener<ApiResult<T>> dealListener, Response.Listener<ApiResult<T>> listener, Response.ErrorListener errorListener, Object tag, int retries){
        return getApi(url, resultType, dealListener, listener, errorListener, tag, retries, -1);
    }
    public static<T> ApiResultRequest getApi(final String url, TypeToken<T> resultType, ApiModelRequest.ResponseDealListener<ApiResult<T>> dealListener, Response.Listener<ApiResult<T>> listener, Response.ErrorListener errorListener, Object tag){
        return getApi(url, resultType, dealListener, listener, errorListener, tag, -1, -1);
    }
    public static<T> ApiResultRequest getApi(final String url, TypeToken<T> resultType, Response.Listener<ApiResult<T>> listener, Response.ErrorListener errorListener, Object tag, int retries, int timeout){
        return getApi(url, resultType, null, listener, errorListener, tag, retries, timeout);
    }
    public static<T> ApiResultRequest getApi(final String url, TypeToken<T> resultType, Response.Listener<ApiResult<T>> listener, Response.ErrorListener errorListener, Object tag, int retries){
        return getApi(url, resultType, null, listener, errorListener, tag, retries, -1);
    }
    public static<T> ApiResultRequest getApi(final String url, TypeToken<T> resultType, Response.Listener<ApiResult<T>> listener, Response.ErrorListener errorListener, Object tag){

        return getApi(url, resultType, null, listener, errorListener, tag, -1, -1);
    }


    public static<T> ApiResultRequest postApi(final String url, TypeToken<T> resultType, Object objRequest, ApiModelRequest.ResponseDealListener<ApiResult<T>> dealListener, Response.Listener<ApiResult<T>> listener, Response.ErrorListener errorListener, Object tag, int retries, int timeout){
        HBLog.w(TAG, "postUrl=========>" + url);
        HBLog.w(TAG, "objRequest:" + objRequest.toString());
        ApiResultRequest<T> request = new ApiResultRequest<>(Request.Method.POST, url, resultType, objRequest, dealListener, listener, errorListener);
        request.setCustomHeaders(getGeneralHeader());
        mReqManager.addToRequestQueue(request, tag, retries, timeout);


        return request;
    }
    public static<T> ApiResultRequest postApi(final String url, TypeToken<T> resultType, Object objRequest, ApiModelRequest.ResponseDealListener<ApiResult<T>> dealListener, Response.Listener<ApiResult<T>> listener, Response.ErrorListener errorListener, Object tag, int retries){
        return postApi(url, resultType, objRequest, dealListener, listener, errorListener, tag, retries, -1);
    }
    public static<T> ApiResultRequest postApi(final String url, TypeToken<T> resultType, Object objRequest, ApiModelRequest.ResponseDealListener<ApiResult<T>> dealListener, Response.Listener<ApiResult<T>> listener, Response.ErrorListener errorListener, Object tag){
        return postApi(url, resultType, objRequest, dealListener, listener, errorListener, tag, -1, -1);
    }
    public static<T> ApiResultRequest postApi(final String url, TypeToken<T> resultType, Object objRequest, Response.Listener<ApiResult<T>> listener, Response.ErrorListener errorListener, Object tag, int retries, int timeout){
        return postApi(url, resultType, objRequest, null, listener, errorListener, tag, retries, timeout);
    }
    public static<T> ApiResultRequest postApi(final String url, TypeToken<T> resultType, Object objRequest, Response.Listener<ApiResult<T>> listener, Response.ErrorListener errorListener, Object tag, int retries){
        return postApi(url, resultType, objRequest, null, listener, errorListener, tag, retries, -1);
    }
    public static<T> ApiResultRequest postApi(final String url, TypeToken<T> resultType, Object objRequest, Response.Listener<ApiResult<T>> listener, Response.ErrorListener errorListener, Object tag){
        return postApi(url, resultType, objRequest, null, listener, errorListener, tag, -1, -1);
    }

    public static<T> ApiResultRequest putApi(final String url, TypeToken<T> resultType, Object objRequest, ApiModelRequest.ResponseDealListener<ApiResult<T>> dealListener, Response.Listener<ApiResult<T>> listener, Response.ErrorListener errorListener, Object tag, int retries, int timeout){
        ApiResultRequest<T> request = new ApiResultRequest<>(Request.Method.PUT, url, resultType, objRequest, dealListener, listener, errorListener);
        request.setCustomHeaders(getGeneralHeader());
        mReqManager.addToRequestQueue(request, tag, retries, timeout);
        return request;
    }
    public static<T> ApiResultRequest putApi(final String url, TypeToken<T> resultType, Object objRequest, ApiModelRequest.ResponseDealListener<ApiResult<T>> dealListener, Response.Listener<ApiResult<T>> listener, Response.ErrorListener errorListener, Object tag, int retries){
        return putApi(url, resultType, objRequest, dealListener, listener, errorListener, tag, retries, -1);
    }
    public static<T> ApiResultRequest putApi(final String url, TypeToken<T> resultType, Object objRequest, ApiModelRequest.ResponseDealListener<ApiResult<T>> dealListener, Response.Listener<ApiResult<T>> listener, Response.ErrorListener errorListener, Object tag){
        return putApi(url, resultType, objRequest, dealListener, listener, errorListener, tag, -1, -1);
    }
    public static<T> ApiResultRequest putApi(final String url, TypeToken<T> resultType, Object objRequest, Response.Listener<ApiResult<T>> listener, Response.ErrorListener errorListener, Object tag, int retries, int timeout){
        return putApi(url, resultType, objRequest, null, listener, errorListener, tag, retries, timeout);
    }
    public static<T> ApiResultRequest putApi(final String url, TypeToken<T> resultType, Object objRequest, Response.Listener<ApiResult<T>> listener, Response.ErrorListener errorListener, Object tag, int retries){
        return putApi(url, resultType, objRequest, null, listener, errorListener, tag, retries, -1);
    }
    public static<T> ApiResultRequest putApi(final String url, TypeToken<T> resultType, Object objRequest, Response.Listener<ApiResult<T>> listener, Response.ErrorListener errorListener, Object tag){
        return putApi(url, resultType, objRequest, null, listener, errorListener, tag, -1, -1);
    }

    public static<T> ApiResultRequest deleteApi(final String url, TypeToken<T> resultType, ApiModelRequest.ResponseDealListener<ApiResult<T>> dealListener, Response.Listener<ApiResult<T>> listener, Response.ErrorListener errorListener, Object tag, int retries, int timeout){
        ApiResultRequest<T> request = new ApiResultRequest<>(Request.Method.DELETE,url, resultType, dealListener, listener, errorListener);
        request.setCustomHeaders(getGeneralHeader());
        mReqManager.addToRequestQueue(request, tag, retries, timeout);
        return request;
    }
    public static<T> ApiResultRequest deleteApi(final String url, TypeToken<T> resultType, ApiModelRequest.ResponseDealListener<ApiResult<T>> dealListener, Response.Listener<ApiResult<T>> listener, Response.ErrorListener errorListener, Object tag, int retries){
        return deleteApi(url, resultType, dealListener, listener, errorListener, tag, retries, -1);
    }
    public static<T> ApiResultRequest deleteApi(final String url, TypeToken<T> resultType, ApiModelRequest.ResponseDealListener<ApiResult<T>> dealListener, Response.Listener<ApiResult<T>> listener, Response.ErrorListener errorListener, Object tag){
        return deleteApi(url, resultType, dealListener, listener, errorListener, tag, -1, -1);
    }
    public static<T> ApiResultRequest deleteApi(final String url, TypeToken<T> resultType, Response.Listener<ApiResult<T>> listener, Response.ErrorListener errorListener, Object tag, int retries, int timeout){
        return deleteApi(url, resultType, null, listener, errorListener, tag, retries, timeout);
    }
    public static<T> ApiResultRequest deleteApi(final String url, TypeToken<T> resultType, Response.Listener<ApiResult<T>> listener, Response.ErrorListener errorListener, Object tag, int retries){
        return deleteApi(url, resultType, null, listener, errorListener, tag, retries, -1);
    }
    public static<T> ApiResultRequest deleteApi(final String url, TypeToken<T> resultType, Response.Listener<ApiResult<T>> listener, Response.ErrorListener errorListener, Object tag){
        return deleteApi(url, resultType, null, listener, errorListener, tag, -1, -1);
    }


    /**
     * Response is T, T is result model you defined or JSONObject, JSONArray
     * @param url
     * @param listener
     * @param errorListener
     * @param dealListener
     * @param tag
     * @param retries
     * @param timeout
     * @param <T>
     */
    public static<T> ApiModelRequest get(final String url, TypeToken<T> resultType, ApiModelRequest.ResponseDealListener<T> dealListener, Response.Listener<T> listener, Response.ErrorListener errorListener, Object tag, int retries, int timeout){
        HBLog.w(TAG, "getUrl=========>" + url);
        ApiModelRequest<T> request = new ApiModelRequest<>(url, resultType, dealListener, listener, errorListener);
        request.setCustomHeaders(getGeneralHeader());
        mReqManager.addToRequestQueue(request, tag, retries, timeout);

        return request;
    }
    public static<T> ApiModelRequest get(final String url, TypeToken<T> resultType, ApiModelRequest.ResponseDealListener<T> dealListener, Response.Listener<T> listener, Response.ErrorListener errorListener, Object tag, int retries){
        return get(url, resultType, dealListener, listener, errorListener, tag, retries, -1);
    }
    public static<T> ApiModelRequest get(final String url, TypeToken<T> resultType,  ApiModelRequest.ResponseDealListener<T> dealListener, Response.Listener<T> listener, Response.ErrorListener errorListener, Object tag){
        return get(url, resultType, dealListener, listener, errorListener, tag, -1, -1);
    }
    public static<T> ApiModelRequest get(final String url, TypeToken<T> resultType, Response.Listener<T> listener, Response.ErrorListener errorListener, Object tag, int retries, int timeout){
        return get(url, resultType, null, listener, errorListener, tag, retries, timeout);
    }
    public static<T> ApiModelRequest get(final String url, TypeToken<T> resultType, Response.Listener<T> listener, Response.ErrorListener errorListener, Object tag, int retries){
        return get(url, resultType, null, listener, errorListener, tag, retries, -1);
    }
    public static<T> ApiModelRequest get(final String url, TypeToken<T> resultType, Response.Listener<T> listener, Response.ErrorListener errorListener, Object tag){
        return get(url, resultType, null, listener, errorListener, tag, -1, -1);
    }


    public static<T> ApiModelRequest post(final String url, TypeToken<T> resultType, Object objRequest, ApiModelRequest.ResponseDealListener<T> dealListener, Response.Listener<T> listener, Response.ErrorListener errorListener, Object tag, int retries, int timeout){
        HBLog.w(TAG, "postUrl=========>" + url);
        HBLog.w(TAG, "objRequest:" + objRequest.toString());
        ApiModelRequest<T> request = new ApiModelRequest<>(Request.Method.POST, url, resultType, objRequest, dealListener, listener, errorListener);
        request.setCustomHeaders(getGeneralHeader());
        mReqManager.addToRequestQueue(request, tag, retries, timeout);
        return request;
    }
    public static<T> ApiModelRequest post(final String url, TypeToken<T> resultType, Object objRequest, ApiModelRequest.ResponseDealListener<T> dealListener, Response.Listener<T> listener, Response.ErrorListener errorListener, Object tag, int retries){
        return post(url, resultType, objRequest, dealListener, listener, errorListener, tag, retries, -1);
    }
    public static<T> ApiModelRequest post(final String url, TypeToken<T> resultType, Object objRequest, ApiModelRequest.ResponseDealListener<T> dealListener, Response.Listener<T> listener, Response.ErrorListener errorListener, Object tag){
        return post(url, resultType, objRequest, dealListener, listener, errorListener, tag, -1, -1);
    }
    public static<T> ApiModelRequest post(final String url, TypeToken<T> resultType, Object objRequest, Response.Listener<T> listener, Response.ErrorListener errorListener, Object tag, int retries, int timeout){
        return post(url, resultType, objRequest, null, listener, errorListener, tag, retries, timeout);
    }
    public static<T> ApiModelRequest post(final String url, TypeToken<T> resultType, Object objRequest, Response.Listener<T> listener, Response.ErrorListener errorListener, Object tag, int retries){
        return post(url, resultType, objRequest, null, listener, errorListener, tag, retries, -1);
    }
    public static<T> ApiModelRequest post(final String url, TypeToken<T> resultType, Object objRequest, Response.Listener<T> listener, Response.ErrorListener errorListener, Object tag){
        return post(url, resultType, objRequest, null, listener, errorListener, tag, -1, -1);
    }

    public static<T> ApiModelRequest put(final String url, TypeToken<T> resultType, Object objRequest, ApiModelRequest.ResponseDealListener<T> dealListener, Response.Listener<T> listener, Response.ErrorListener errorListener, Object tag, int retries, int timeout){
        ApiModelRequest<T> request = new ApiModelRequest<>(Request.Method.PUT, url, resultType, objRequest, dealListener, listener, errorListener);
        request.setCustomHeaders(getGeneralHeader());
        mReqManager.addToRequestQueue(request, tag, retries, timeout);
        return request;
    }
    public static<T> ApiModelRequest put(final String url, TypeToken<T> resultType, Object objRequest, ApiModelRequest.ResponseDealListener<T> dealListener, Response.Listener<T> listener, Response.ErrorListener errorListener, Object tag, int retries){
        return put(url, resultType, objRequest, dealListener, listener, errorListener, tag, retries, -1);
    }
    public static<T> ApiModelRequest put(final String url, TypeToken<T> resultType, Object objRequest, ApiModelRequest.ResponseDealListener<T> dealListener, Response.Listener<T> listener, Response.ErrorListener errorListener, Object tag){
        return put(url, resultType, objRequest, dealListener, listener, errorListener, tag, -1, -1);
    }
    public static<T> ApiModelRequest put(final String url, TypeToken<T> resultType, Object objRequest, Response.Listener<T> listener, Response.ErrorListener errorListener, Object tag, int retries, int timeout){
        return put(url, resultType, objRequest, null, listener, errorListener, tag, retries, timeout);
    }
    public static<T> ApiModelRequest put(final String url, TypeToken<T> resultType, Object objRequest, Response.Listener<T> listener, Response.ErrorListener errorListener, Object tag, int retries){
        return put(url, resultType, objRequest, null, listener, errorListener, tag, retries, -1);
    }
    public static<T> ApiModelRequest put(final String url, TypeToken<T> resultType, Object objRequest, Response.Listener<T> listener, Response.ErrorListener errorListener, Object tag){
        return put(url, resultType, objRequest, null, listener, errorListener, tag, -1, -1);
    }


    public static<T> ApiModelRequest delete(final String url, TypeToken<T> resultType, ApiModelRequest.ResponseDealListener<T> dealListener, Response.Listener<T> listener, Response.ErrorListener errorListener, Object tag, int retries, int timeout){
        ApiModelRequest<T> request = new ApiModelRequest<>(Request.Method.DELETE,url, resultType, dealListener, listener, errorListener);
        request.setCustomHeaders(getGeneralHeader());
        mReqManager.addToRequestQueue(request, tag, retries, timeout);
        return request;
    }
    public static<T> ApiModelRequest delete(final String url, TypeToken<T> resultType, ApiModelRequest.ResponseDealListener<T> dealListener, Response.Listener<T> listener, Response.ErrorListener errorListener, Object tag, int retries){
        return delete(url, resultType, dealListener, listener, errorListener, tag, retries, -1);
    }
    public static<T> ApiModelRequest delete(final String url, TypeToken<T> resultType,  ApiModelRequest.ResponseDealListener<T> dealListener, Response.Listener<T> listener, Response.ErrorListener errorListener, Object tag){
        return delete(url, resultType, dealListener, listener, errorListener, tag, -1, -1);
    }
    public static<T> ApiModelRequest delete(final String url, TypeToken<T> resultType, Response.Listener<T> listener, Response.ErrorListener errorListener, Object tag, int retries, int timeout){
        return delete(url, resultType, null, listener, errorListener, tag, retries, timeout);
    }
    public static<T> ApiModelRequest delete(final String url, TypeToken<T> resultType, Response.Listener<T> listener, Response.ErrorListener errorListener, Object tag, int retries){
        return delete(url, resultType, null, listener, errorListener, tag, retries, -1);
    }
    public static<T> ApiModelRequest delete(final String url, TypeToken<T> resultType, Response.Listener<T> listener, Response.ErrorListener errorListener, Object tag){
        return delete(url, resultType, null, listener, errorListener, tag, -1, -1);
    }

//    public static<T> ImageUploadRequest uploadImage(String url, Bitmap bitmap, TypeToken<T> resultType, ApiModelRequest.ResponseDealListener<ApiResult<T>> dealListener, Response.Listener<ApiResult<T>> listener, Response.ErrorListener errorListener, Object tag,
//                                                    String fileField, String fileName, String fileType){
//        ImageUploadRequest<T> request = new ImageUploadRequest<>(url, resultType, dealListener, listener, errorListener, bitmap, fileField, fileName, fileType);
//        mReqManager.addToRequestQueue(request, tag);
//        return request;
//    }
//
//    public static<T> ImageUploadRequest uploadImage(String url, Bitmap bitmap, TypeToken<T> resultType, Response.Listener<ApiResult<T>> listener, Response.ErrorListener errorListener, Object tag, String fileField, String fileName, String fileType){
//        return uploadImage(url, bitmap, resultType, null, listener, errorListener, tag, fileField, fileName, fileType);
//    }
//
//
//    public static<T> ImageUploadRequest uploadImage(String url, Bitmap bitmap, TypeToken<T> resultType, ApiModelRequest.ResponseDealListener<ApiResult<T>> dealListener,  Response.Listener<ApiResult<T>> listener, Response.ErrorListener errorListener, Object tag){
//        return uploadImage(url, bitmap, resultType, dealListener, listener, errorListener, tag, "headImg", "headImg", "image/jpeg");
//    }
//
//    public static<T> ImageUploadRequest uploadImage(String url, Bitmap bitmap, TypeToken<T> resultType, Response.Listener<ApiResult<T>> listener, Response.ErrorListener errorListener, Object tag){
//        return uploadImage(url, bitmap, resultType, null, listener, errorListener, tag, "headImg", "headImg", "image/jpeg");
//    }
//
//    public static<T> ImageUploadRequest uploadImage(String url, byte[] bytes, TypeToken<T> resultType, ApiModelRequest.ResponseDealListener<ApiResult<T>> dealListener, Response.Listener<ApiResult<T>> listener, Response.ErrorListener errorListener, Object tag,
//                                                    String fileField, String fileName, String fileType){
//        ImageUploadRequest<T> request = new ImageUploadRequest<>(url,bytes, resultType, dealListener, listener, errorListener, fileField, fileName, fileType);
//        mReqManager.addToRequestQueue(request, tag);
//        return request;
//    }
//
//
//    public static StringRequest getString(String url, Response.Listener<String> listener, Response.ErrorListener errorListener, Object tag){
//        StringRequest stringRequest = new StringRequest(Request.Method.GET, url, listener, errorListener);
//        RequestManager.getInstance().addToRequestQueue(stringRequest, tag);
//        return stringRequest;
//    }

    private static String getUniUserId(Context context) {
        if (deviceUniqueId == null) {
            final TelephonyManager tm = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);

            final String tmDevice, mac, serial;
            tmDevice = "" + tm.getDeviceId();
            mac = ""+ getLocalMacAddress(context);
//            androidId = "" + android.provider.Settings.Secure.getString(context.getContentResolver(), android.provider.Settings.Secure.ANDROID_ID);
            serial = Build.SERIAL;

            UUID deviceUuid = new UUID(serial.hashCode(), ((long)tmDevice.hashCode() << 32) | mac.hashCode());
            deviceUniqueId = MD5Util.getMD5String(deviceUuid.toString());

        }
        return deviceUniqueId;
    }

    public static String getLocalMacAddress(Context context) {
        try{
            WifiManager wifi = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
            WifiInfo info = wifi.getConnectionInfo();
            return info.getMacAddress();
        }catch (Exception e) {
            // TODO: handle exception
        }
        return null;
    }

    public interface OnHeaderGenerateListener{
        public void onHeadGenerate(HashMap<String, String> generalHeaders);
    }
    private static OnHeaderGenerateListener mListener = null;
    public static void setOnHeadGenerateListener(OnHeaderGenerateListener listener){
        mListener = listener;
    }
}
